﻿/***************************************************/
/*** file:RingBuffer.cs                          ***/
/*** date:2010-08-07                             ***/
/*** author:huas                                 ***/
/*** function:implement ring buffer              ***/
/***************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IM.Common
{
    public class RingBuffer
    {
        private int m_nBeginIndex;
        private int m_nEndIndex;
        private int m_nSize;
        private byte[] m_buffer;
        
        private const int DEFUALT_BUF_SIZE = 1024 * 1024; //默认1M大小
        private const int DEFUALT_SPACE_SIZE = 8;         //8字节的空隙

        public RingBuffer(int nBufSize)
        {
            if (nBufSize <= 0)
            {
                nBufSize = DEFUALT_BUF_SIZE;
            }
            m_nSize = nBufSize;
            m_buffer = new byte[m_nSize];
            
            m_nBeginIndex = 0;
            m_nEndIndex = 0;
        }

        //return -1 : buffer is empty
        //return -2 : data crash
        //return -3 : param error
        //return -4 : szData size is not enough
        //return  0 : ok
        public int GetOnePack(byte[] szData, ref int nBufSize)
        {
            int nTmpBeginIndex = m_nBeginIndex;
            int nTmpEndIndex = m_nEndIndex;
            if (szData == null || nBufSize <= 0)
            {
                return -3;
            }
            if (nTmpBeginIndex == nTmpEndIndex)
            {
                return -1;
            }

            //get length
            int nLength = BitConverter.ToInt32(m_buffer, nTmpBeginIndex);
            if (nLength <= 0 || nLength >= m_nSize)
            {
                m_nBeginIndex = m_nEndIndex = 0;
                return -2;
            }
            if (nLength > nBufSize)
            {
                return -4;
            }
            nBufSize = nLength;
            nTmpBeginIndex += 4;

            //get data
            if (nTmpEndIndex > nTmpBeginIndex)
            {
                Array.Copy(m_buffer, nTmpBeginIndex, szData, 0, nBufSize);
            }
            else
            {
                Array.Copy(m_buffer, nTmpBeginIndex, szData, 0, m_nSize - nTmpBeginIndex);
                Array.Copy(m_buffer, 0, szData, m_nSize - nTmpBeginIndex, nBufSize - (m_nSize - nTmpBeginIndex));
            }
            nTmpBeginIndex = (nTmpBeginIndex + nBufSize) % m_nSize;
            m_nBeginIndex = nTmpBeginIndex;

            return 0;
        }

        //return -1:buffer is full.
        //return -2:data crash
        //return -3:param error
        //return  0:ok
        public int AppendOnePack(byte[] szData, int nDataLen)
        {
            int nTempBegin = m_nBeginIndex;
            int nTempEnd = m_nEndIndex;

            if (szData == null || nDataLen <= 0)
            {
                return -3;
            }
            if (IsFull())
            {
                return -1;
            }

            int nMaxUnusedLen = 0;
            if (nTempBegin > nTempEnd)
            {
                nMaxUnusedLen = nTempBegin - nTempEnd;
            }
            else
            {
                nMaxUnusedLen = m_nSize - (nTempEnd - nTempBegin);
            }
            nMaxUnusedLen -= DEFUALT_SPACE_SIZE;

            if (nMaxUnusedLen < nDataLen + 4)
            {
                return -1;
            }

            byte[] byteLen = BitConverter.GetBytes(nDataLen);
            //写入长度
            Array.Copy(byteLen, 0, m_buffer, nTempEnd, 4);
            nTempEnd += 4;

            //写入数据
            if (nTempBegin <= nTempEnd)
            {
                if (m_nSize - nTempEnd >= nDataLen)
                {
                    Array.Copy(szData, 0, m_buffer, nTempEnd, nDataLen);
                }
                else
                {
                    Array.Copy(szData, 0, m_buffer, nTempEnd, m_nSize - nTempEnd);
                    Array.Copy(szData, m_nSize - nTempEnd, m_buffer, 0, nDataLen - (m_nSize - nTempEnd));
                }
            }
            else 
            {
                Array.Copy(szData, 0, m_buffer, nTempEnd, nDataLen);
            }

            nTempEnd = (nTempEnd + nDataLen) % m_nSize;
            m_nEndIndex = nTempEnd;
            return 0;
        }

        public  int GetUsedLen(ref int nTotalSize)
        {
            int nTempBegin = m_nBeginIndex;
            int nTempEnd = m_nEndIndex;
            int nUsedSize = 0;

            if (nTempEnd >= nTempBegin)
            {
                nUsedSize = nTempEnd - nTempBegin;
            }
            else
            {
                nUsedSize = m_nSize - (nTempBegin - nTempEnd);
            }
            nTotalSize = m_nSize;
            return nUsedSize;
        }

        public bool IsEmpty()
        {
            if (m_nBeginIndex == m_nEndIndex)
            {
                return true;
            }
            return false;
        }

        private bool IsFull()
        {
            int nTempBegin = m_nBeginIndex;
            int nTempEnd = m_nEndIndex;
            int nMaxUnusedLen = 0;

            if (nTempEnd >= nTempBegin)
            {
                nMaxUnusedLen = m_nSize - (nTempEnd - nTempBegin);
            }
            else
            {
                nMaxUnusedLen = nTempBegin - nTempEnd;
            }

            nMaxUnusedLen -= DEFUALT_SPACE_SIZE;
            if (nMaxUnusedLen >= 0)
            {
                return false;
            }
            return true;
        }
    }
}
